# 18. 函数的进阶

# 函数参数---动态传参

如果需要给一个函数传参,面参数又是不确定的,或者我给一个函数传很多参数,那这样形参就要写很多,也很麻烦,这时就可以考虑使用动态传参

动态传参一时爽,重构代码火葬场

这也是形参的第三种:动态参数

# 动态位置参数

动态接收参数的时候要注意:动态参数必须在位置参数的后面

注意:动态位置参数,获取到的值是以元组类型表示

格式:

函数 (*形参):

# 位置参数

def so(a,s,d,*b):
    print(a,s,d,b)
so("江","陈","许","刘","龙","莫")

执行结果:
江 陈 许 ('刘', '龙', '莫')

​ 以上实例,如果把动态参数放在前面,那么所有的实参将都会在动态参数中。

# 默认值参数

如果默认值参数在动态参数前面,那么默认值参数就无法使用默认值

如果默认值参数在动态参数后面,那么默认值参数也是一直都是默认值,除非使用关键字参数、

def so(a,s,d,c="孙",*b):
    print(a,s,d,c,b)
so("江","陈","许","刘","龙","莫")

执行结果:
江 陈 许 刘 ('龙', '莫')

​ 以上实例,如果要给动态参数多个值,那么默认值参数在动态参数前面,这样的话,默认值参数是无法

def so(a,s,d,*b,c="孙"):
    print(a,s,d,c,b)
so("江","陈","许","刘","龙","莫")

执行结果:
江 陈 许 孙 ('刘', '龙', '莫')

​ 以上实例,如果默认值参数在动态参数后面的话,那无法给默认值赋值,因为无论怎么赋值,其值还是会赋给动态参数

def so(a,s,d,*b,c="孙"):
    print(a,s,d,c,b)
so("江","陈","许","刘","龙","莫",c = "周")

执行结果:
江 陈 许 周 ('刘', '龙', '莫')

​ 以上实例,如果默认值参数在动态参数后面的话,可以使用关键字参数来赋值给默认值参数

也许会不会这样想,默认值参数在动态参数前面,在用关键字赋值给默认值参数就行了

def so(a,s,d,c="孙",*b):
    print(a,s,d,c,b)
so("江","陈","许",c = "周","刘","龙","莫")

执行结果:
SyntaxError: positional argument follows keyword argument

======

def so(a,s,d,c="孙",*b):
    print(a,s,d,c,b)
so("江","陈","许","刘","龙","莫",c = "周")

执行结果:
TypeError: so() got multiple values for argument 'c'

# 动态关键字参数

动态关键字参数最好就是放在后面

动态关键字,获取到的值会以字典类型表示

格式:

def 函数(**kra):
def so(a,*s,**d):
    print(a,s,d)
so("江","陈","许",ss="龙",sq="莫")

执行结果:
江 ('陈', '许') {'ss': '龙', 'sq': '莫'}

# 动态参数的另一种传参方式

列表方式

def so(*so):
    print(so)
wo = ["江","陈","许"]
so(wo[0],wo[1],wo[2])
so(*wo)

执行结果:
('江', '陈', '许')
('江', '陈', '许')

​ 以上实例:可迭代对象前面加个 * 运算符将列表打散,并传入形参中

字符串方式

def so(*so):
    print(so)
wo = "你好世界"
so(*wo)

执行结果:
('你', '好', '世', '界')

​ 以上实例:可迭代对象前面加个 * 运算符将字符串打散,并传入形参中

字典方式

def so(*so):
    print(so)
wo = {"姓":"江","名":"凡"}
so(*wo)

执行结果:
('姓', '名')

​ 以上实例:可迭代对象前面加个 * 运算符将字典中的key值打散,并传入形参中

​ 如果想要key值跟value值都打散,请看下一个实例

def so(**so):
    print(so)
wo = {"姓": "江", "名": "凡"}
so(**wo)

执行结果:
{'姓': '江', '名': '凡'}

​ 以上实例,想要让字典中的key值跟value值都打散并传入形参中,就要使用动态关键字参数和可迭代对象前面加二个 **

# 命名空间

Python解释器开始执行之后,就会在内存中开辟一个空间

每当遇到一个变量的时候,就把变量名和值之间的关系记录下来,

但是当遇到函数定义的时候,解释器只是把函数名读入内存,表示这个函数以存在,

至于函数内部的变量和逻辑,解释器是不关心的,也就是说一开始的时候函数只是加载进来,仅此而已,只有当函数被调用和访问的时候,解释器才会根据函数内部声明的变量来开辟变量的内部空间。

注意:随着函数执行完毕,这些函数内部变量也会随着函数执行完毕而被清空

def so():
    ss = 10
so()
print(ss)

执行结果:
NameError: name 'ss' is not defined  ##变量不存在

Python存储的变量,函数,内置函数等的空间,称为命名空间

变量在存储的时候就是存储在命名空间中的

命名空间分类:

  1. 全局命名空间:在python中,函数外,声明的变量都属于全局命名空间
  2. 局部命名空间:在函数中声明的变量都属于局部命名空间
  3. 内置命名空间:Python内部定义或模块定义的内置函数都属于内置命名空间

Python解释器加载命名空间顺序:

  1. 内置命名空间
  2. 全局命令空间
  3. 局部命名空间(函数被执行时)

Python解释器执行命名空间取值顺序:

  1. 局部命名空间
  2. 全局命名空间
  3. 内置命名空间
ss = 20
def so():
    ss = 10
    print("在so函数中的ss变量:",ss)
so()

执行结果:
在so函数中的ss变量: 10

# 作用域

作用域,作用域就是作用范围,按照生效范围来看分为全局作用域和局部作用域

  1. 全局作用域:包含内置命名空间和全局命名空间,在Python解释器的任何位置都可以使用(Python解释器遵循从上到下逐行执行)
  2. 局部作用域:在函数内部可以使用

作用域命令空间

  1. 全局作用域:全局命名空间跟内置命名空间
  2. 局部作用域:局部命名空间

# 查看全局作用域内容 - globals()

通过globals()函数来查看全局作用域中的内容

格式:print(globals())

def so():
    print(globals())
so()
print(globals())

执行结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000013CE10EA278>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/python/PyCharm资源/one/2. python-函数/函数进阶.py', '__cached__': None, 'division': _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192), 'so': <function so at 0x0000013CE0FE1EA0>}

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000013CE10EA278>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/python/PyCharm资源/one/2. python-函数/函数进阶.py', '__cached__': None, 'division': _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192), 'so': <function so at 0x0000013CE0FE1EA0>}

​ 以上实例,从这里就可以看出,globals()函数无论是在全局命名空间或局部命名空间执行都是输出全局作用域中的内容

# 查看当前作用域内容 - locals()

通过locals()函数来查看全局作用域中的内容

格式:print(locals())

def so():
    print(locals())
so()
print(locals())

执行结果:
{}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002126EA7A278>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/python/PyCharm资源/one/2. python-函数/函数进阶.py', '__cached__': None, 'division': _Feature((2, 2, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 8192), 'so': <function so at 0x000002126EA21EA0>}

​ 以上实例,从这里就可以看出,locals()函数在那个命令空间中执行就会输出那个命名空间的作用域内容

998

# 函数的嵌套

只要是遇到了带()的基本上就是函数的调用,如果没有带()就不是函数的调用

函数的执行顺序

def so():
    so = 10
    print(so)
    def ss():
        print(so)
    ss()
so()

执行结果:
10
10

# 全局变量引用 - global()

global()是引入全局变量到局部使用,使用完毕后全局变量返回全局,如果有修改,修改的结果也会生效

global()如果引入全局变量,但是这个全局没有,那么他就会定义如果有这个变量就会返回到全局变量中

格式:global 变量

全局变量存在

s = 1
def so():
    global s
    s =  s + 10
    print(s)
so()
print(s)

执行结果:
11
11

全局变量不存在

def so():
    global s
    s = 10
    print(s)
so()
print(s)

执行结果:
10
10

注意了

def so():
    global s
    s =  s + 10
    print(s)
so()
print(s)

执行结果:
NameError: name 's' is not defined  还没定义s

​ 因为global()是将变量引入局部中,同时声明在这局部中的这个变量将会返回到全局,在以上的实例,因为全局中没有先声明变量,globa()的作用只是声明这个变量会返回全局,并没有生成这个变量,所以不能直接加

# 局部变量引用 - nonlocal()

nonlocal()是引用局部变量,全局变量无法引用,默认是引用上一层的局部变量,如果上一层没有就会一层接一层找,都没有的话会报错

格式:

def so():
    ss = 10
    def wo():
        se = 11
        nonlocal ss
        print(ss)
    wo()
so()

执行结果:
10

# 最后来个嵌套题

a = 1
def fun_1():
    a = 2
    def fun_2():
        nonlocal a
        a = 3
        def fun_3():
            a = 4
            print(a)
        print(a)
        fun_3()
        print(a)
    print(a)
    fun_2()
    print(a)
print(a)
fun_1()
print(a)

手动分析出来结果,答案在最下面

# 答案

1
2
3
4
3
3
1